﻿using System;
using UnityEngine;

[Serializable]
public class SmartTimer
{
    /// <summary>
    /// 计时器当前时间
    /// </summary>
    public float m_fTimer = 0;
    /// <summary>
    /// 计时器最大时间，-1表示递增计时器，>0表示递减计时器
    /// </summary>
    public float m_fMaxTimer = -1;
    /// <summary>
    /// 计时器是否处于激活状态
    /// </summary>
    public bool m_bActive = false;

    /// <summary>
    /// 复制当前SmartTimer实例
    /// </summary>
    /// <returns>返回一个新的SmartTimer实例，其属性值与当前实例相同</returns>
    public SmartTimer Copy()
    {
        var value = new SmartTimer();
        value.m_fMaxTimer = m_fMaxTimer;
        value.m_fTimer = m_fTimer;
        value.m_bActive = m_bActive;
        return value;
    }

    /// <summary>
    /// 检查计时器是否处于激活状态
    /// </summary>
    /// <returns>如果计时器处于激活状态，则返回true；否则返回false</returns>
    public bool IsActive() => m_bActive;

    /// <summary>
    /// 获取计时器的最大时间
    /// </summary>
    /// <returns>返回计时器的最大时间</returns>
    public float GetMax() => m_fMaxTimer;

    /// <summary>
    /// 获取计时器当前时间
    /// </summary>
    /// <returns>返回计时器当前时间</returns>
    public float GetTimer() => m_fTimer;

    public float GetLast()
    {
        if (m_fMaxTimer >= 0)
            return m_fMaxTimer - m_fTimer;
        return 0;
    }

    /// <summary>
    /// 获取计时器的进度比例
    /// </summary>
    /// <returns>如果m_fMaxTimer>0，则返回m_fTimer/m_fMaxTimer，否则返回0</returns>
    public float GetScale()
    {
        if (m_fMaxTimer > 0)
            return Math.Min(m_fTimer / m_fMaxTimer, 1); // 防止超出范围
        return 0;
    }

    /// <summary>
    /// 上次更新时间
    /// </summary>
    private float m_fLastTime = 0;
    /// <summary>
    /// 时间差
    /// </summary>
    private float m_fDeltaTime = 0;

    /// <summary>
    /// 获取时间差
    /// </summary>
    /// <param name="bReal">如果为true，则返回实际时间差，否则返回Time.deltaTime</param>
    /// <returns>返回时间差</returns>
    public float GetDelta(bool bReal = false)
    {
        if (bReal)
            return m_fDeltaTime;
        return Time.deltaTime;
    }

    /// <summary>
    /// 修改基础类实例
    /// </summary>
    private ModifyBase m_Modify = new ModifyBase();

    /// <summary>
    /// 获取修改基础类实例
    /// </summary>
    /// <returns>返回ModifyBase实例</returns>
    public ModifyBase Modify => m_Modify;

    /// <summary>
    /// 执行更新操作，适用于周期性任务
    /// </summary>
    /// <param name="num">周期计数，记录执行次数</param>
    /// <param name="fScale">时间缩放因子</param>
    /// <returns>如果计时器达到最大时间，则返回true，否则返回false</returns>
    public bool DoUpdate(ref int num, float fScale = 1)
    {
        if (!m_bActive || m_fMaxTimer < 0) return false;

        m_fTimer -= GetDelta(false) * fScale;
        if (m_fTimer >= 0) return false;

        // 计算周期执行次数
        do
        {
            m_fTimer += m_fMaxTimer;
            ++num;
        } while (m_fTimer < 0);

        return true;
    }

    public void DoUpdateRepeat(float fTimeScale = 1, bool bReal = false, Action callBack = null)
    {
        if (!m_bActive)
            return;
        if (DoUpdate(fTimeScale, bReal))
        {
            DoRestart();
            if (callBack != null)
                callBack();
        }
    }
    /// <summary>
    /// 执行更新操作
    /// </summary>
    /// <param name="fTimeScale">时间缩放因子</param>
    /// <param name="bReal">是否使用实际时间差</param>
    /// <returns>如果计时器结束，则返回true，否则返回false</returns>
    public bool DoUpdate(float fTimeScale = 1, bool bReal = false)
    {
        if (!m_bActive) return false;

        m_fDeltaTime = (Time.realtimeSinceStartup - m_fLastTime) * Time.timeScale;
        m_fLastTime = Time.realtimeSinceStartup;

        int cur = (int)m_fTimer;

        if (m_fMaxTimer < 0)
        {
            m_fTimer += GetDelta(bReal) * fTimeScale;
        }
        else if (m_fMaxTimer >= 0)
        {
            m_fTimer -= GetDelta(bReal) * fTimeScale;
            if (m_fTimer < 0)
            {
                m_fTimer = 0;
                m_bActive = false;
                Modify.MakeModify();
                return true;
            }
        }

        if (cur != (int)m_fTimer)
            Modify.MakeModify();

        return false;
    }

    /// <summary>
    /// 停止计时器
    /// </summary>
    public void DoStop() => m_bActive = false;

    /// <summary>
    /// 关闭计时器，重置时间到0
    /// </summary>
    public void DoClose()
    {
        m_fTimer = 0;
        m_bActive = false;
    }

    /// <summary>
    /// 重置计时器到初始状态
    /// </summary>
    public void DoReset()
    {
        if (m_fMaxTimer > 0)
            m_fTimer = m_fMaxTimer;
        else
            m_fTimer = 0;

        m_bActive = true;
        m_fLastTime = Time.realtimeSinceStartup;
        Normalize();
        Modify.MakeModify();
    }

    public void DoInit(float fTime)
    {
        DoReset(fTime);
        DoClose();
    }
    /// <summary>
    /// 重新启动计时器
    /// </summary>
    /// <returns>如果计时器达到最大时间，则返回true，否则返回false</returns>
    public bool DoRestart()
    {
        if (m_fMaxTimer > 0)
        {
            m_fTimer += m_fMaxTimer;
            if (m_fTimer < 0)
            {
                DoClose();
                return true;
            }
        }
        else
            m_fTimer = 0;

        m_bActive = true;
        m_fLastTime = Time.realtimeSinceStartup;
        return false;
    }

    /// <summary>
    /// 重置计时器并设置最大时间
    /// </summary>
    /// <param name="fTime">要设置的最大时间</param>
    public void DoReset(float fTime)
    {
        SetMax(fTime);
        DoReset();
    }

    /// <summary>
    /// 设置计时器数据
    /// </summary>
    /// <param name="fTime">计时器当前时间</param>
    /// <param name="fMaxTime">计时器最大时间</param>
    public void SetData(float fTime, float fMaxTime = -1)
    {
        if (fMaxTime >= 0)
            m_fMaxTimer = fMaxTime;

        if (fTime >= 0)
            m_fTimer = fTime;

        m_bActive = true;
        m_fLastTime = Time.realtimeSinceStartup;
        Normalize();
    }

    /// <summary>
    /// 重写ToString方法
    /// </summary>
    /// <returns>返回计时器当前时间与最大时间的字符串表示</returns>
    public override string ToString() => $"{m_fTimer}/{m_fMaxTimer}";

    /// <summary>
    /// 标准化计时器时间
    /// </summary>
    public void Normalize()
    {
        if (m_fMaxTimer > 0)
        {
            m_fTimer = Math.Max(0, Math.Min(m_fTimer, m_fMaxTimer));
        }
        else
        {
            m_fTimer = Math.Max(0, m_fTimer);
        }
    }

    /// <summary>
    /// 设置计时器最大时间
    /// </summary>
    /// <param name="value">要设置的最大时间</param>
    public void SetMax(float value)
    {
        if (value >= 0)
            m_fMaxTimer = value;
    }

    /// <summary>
    /// 结束计时器，将当前时间设置为0
    /// </summary>
    public void DoEnd() => m_fTimer = 0;
}